using System.Collections.Generic;
using System.Linq;
using HCore.IOC.Beans.Factory.Attribute.Attr;
using HCore.IOC.Context.Stereotype;
using HCore.Tests.Runtime.ShootingEditor2D.Scripts.Support.CommandQuery;
using JetBrains.Annotations;

namespace HCore.Tests.Runtime.ShootingEditor2D.Scripts.System.GunSystem
{
    /// <summary>
    ///     枪械系统接口，定义了枪械管理相关的核心功能
    /// </summary>
    public interface IGunSystem
    {
        /// <summary>
        ///     当前枪信息
        /// </summary>
        GunInfo CurrentGun { get; }

        /// <summary>
        ///     当前枪的配置信息
        /// </summary>
        GunConfigItem CurrentGunConfigItem { get; }

        /// <summary>
        ///     枪械列队
        /// </summary>
        Queue<GunInfo> GunInfos { get; }

        /// <summary>
        ///     能否换枪
        /// </summary>
        /// <returns>是否可以切换枪械</returns>
        bool CanShiftGun { get; }

        /// <summary>
        ///     捡枪
        /// </summary>
        /// <param name="gunName">枪械名称</param>
        /// <param name="bulletCountInGun">枪内子弹数量</param>
        /// <param name="bulletCountOutGun">枪外备用子弹数量</param>
        void PickGun(string gunName, int bulletCountInGun, int bulletCountOutGun);

        /// <summary>
        ///     换枪
        /// </summary>
        void ShiftGun();


        /// <summary>
        ///     根据枪械名称获取对应枪械的配置数据
        /// </summary>
        /// <param name="gunName">枪械名称</param>
        /// <returns>枪械配置项</returns>
        GunConfigItem GetGunConfigByName(string gunName);
    }


    /// <summary>
    ///     枪械系统实现类，负责枪械的拾取、切换等核心逻辑
    /// </summary>
    [Component]
    public class GunSystem : CommandQueryEventSender, IGunSystem
    {
        [Autowired] private GunConfig mGunConfig;

        /// <summary>
        ///     获取当前枪械信息
        /// </summary>
        public GunInfo CurrentGun { get; } = new();

        /// <summary>
        ///     获取枪械队列，用于存储备用枪械
        /// </summary>
        public Queue<GunInfo> GunInfos { get; } = new();

        /// <summary>
        ///     获取是否可以切换枪械的状态
        /// </summary>
        public bool CanShiftGun => GunInfos.Count > 0;

        /// <summary>
        ///     捡枪逻辑处理
        /// </summary>
        /// <param name="gunName">枪械名称</param>
        /// <param name="bulletCountInGun">枪内子弹数量</param>
        /// <param name="bulletCountOutGun">枪外备用子弹数量</param>
        public void PickGun(string gunName, int bulletCountInGun, int bulletCountOutGun)
        {
            // 已经有这把枪了，则直接把捡到枪的子弹附加到当前枪的备用子弹上
            if (string.Equals(CurrentGun.Name.Value, gunName))
            {
                CurrentGun.BulletCountOutGun.Value += bulletCountInGun;
                CurrentGun.BulletCountOutGun.Value += bulletCountOutGun;
            }
            else
            {
                var cacheGun = GunInfos.FirstOrDefault(gun => string.Equals(gun.Name.Value, gunName));
                // 队列已经有这把枪，也则直接把捡到枪的子弹附加到枪的备用子弹上 
                if (cacheGun != null)
                {
                    cacheGun.BulletCountOutGun.Value += bulletCountInGun;
                    cacheGun.BulletCountOutGun.Value += bulletCountOutGun;
                }
                // 列队没有这把枪，则切枪
                else
                {
                    // 捡枪马上切，是唯一可以忽略冷却的。其它的切枪，需要cd
                    ChangeGun(gunName, bulletCountInGun, bulletCountOutGun);
                }
            }
        }

        /// <summary>
        ///     执行枪械切换操作
        /// </summary>
        public void ShiftGun()
        {
            // 非闲置状态，不能切枪
            if (CurrentGun.GunState.Value != GunState.Idle
                // 不能切枪
                || !CanShiftGun) return;

            var gun = GunInfos.Dequeue();
            ChangeGun(gun.Name.Value, gun.BulletCountInGun.Value, gun.BulletCountOutGun.Value);
        }


        /// <summary>
        ///     获取当前枪械的配置信息
        /// </summary>
        public GunConfigItem CurrentGunConfigItem => GetGunConfigByName(CurrentGun.Name.Value);


        /// <summary>
        ///     根据枪械名称获取对应的配置项
        /// </summary>
        /// <param name="gunName">枪械名称</param>
        /// <returns>返回指定名称的枪械配置项，如果未找到则返回null</returns>
        [CanBeNull]
        public GunConfigItem GetGunConfigByName(string gunName)
        {
            return mGunConfig.GetItemByName(gunName);
        }


        /// <summary>
        ///     执行枪械更换的核心逻辑
        /// </summary>
        /// <param name="gunName">新枪械名称</param>
        /// <param name="bulletCountInGun">新枪内子弹数量</param>
        /// <param name="bulletCountOutGun">新枪外备用子弹数量</param>
        private void ChangeGun(string gunName, int bulletCountInGun, int bulletCountOutGun)
        {
            ///// 缓存一下当前的枪
            var cloneCurrentGun = CurrentGun.Clone() as GunInfo;
            // 把当前的枪信息缓存起来
            GunInfos.Enqueue(cloneCurrentGun);

            ///// 换枪
            CurrentGun.Name.Value = gunName;
            CurrentGun.BulletCountInGun.Value = bulletCountInGun;
            CurrentGun.BulletCountOutGun.Value = bulletCountOutGun;

            // 发送换枪事件
            SendEvent(new GunChangedEvent(gunName, cloneCurrentGun!.Name.Value));
        }
    }

    /// <summary>
    ///     换枪事件，用于通知系统枪械已切换
    /// </summary>
    public class GunChangedEvent
    {
        /// <summary>
        ///     构造函数，初始化换枪事件
        /// </summary>
        /// <param name="newGunName">新枪械名称</param>
        /// <param name="oldGunName">旧枪械名称</param>
        public GunChangedEvent(string newGunName, string oldGunName)
        {
            NewGunName = newGunName;
            OldGunName = oldGunName;
        }

        /// <summary>
        ///     获取旧枪械名称
        /// </summary>
        public string OldGunName { get; private set; }

        /// <summary>
        ///     获取新枪械名称
        /// </summary>
        public string NewGunName { get; private set; }
    }
}